# _*_ coding: utf-8 _*_
"""
author: lianGang
email: 1542652663@qq.com
"""

""""
减少可调用对象的参数个数
"""


# def spam(a, b, c, d):
#     print(a, b, c, d)
#
# from functools import partial
#
# s1 = partial(spam, 1)
#
# points = [ (1, 2), (3, 4), (5, 6), (7, 8) ]
#
# import math
#
# def distance(p1, p2):
#     x1, y1 = p1
#     x2, y2 = p2
#     return math.hypot(x2-x1, y2-y1)
#
# pt = (4,3)
# points.sort(key=partial(distance, pt))
# print(points)

# partial 用于回调函数
# def output_result(result, log=None):
#     if log is not None:
#         log.debug('Got: %r', result)
#
#
# def add(x, y):
#     return x+y
#
# if __name__ == '__main__':
#     import logging
#     from multiprocessing import Pool
#     from functools import partial
#
#     logging.basicConfig(level=logging.DEBUG)
#     log = logging.getLogger('test')
#
#     p = Pool()
#     p.apply_async(add, (3,4), callback=partial(output_result, log=log))
#     p.close()
#     p.join()


# echo服务器 减少参数
# from socketserver import StreamRequestHandler, TCPServer
#
# class EchoHandler(StreamRequestHandler):
#     def __init__(self, *args, ack, **kwargs):
#         self.ack = ack
#         super().__init__(*args, **kwargs)
#
#     def handle(self):
#         for line in self.rfile:
#             self.wfile.write(b'GOT:' + line)
#
# from functools import partial
#
# serv = TCPServer(('', 15000),partial(EchoHandler, ack=b'RECEIVED:'))
# serv.serve_forever()
#

# 将单方法的类转换位函数
from urllib.request import urlopen

class UrlTemplate:
    def __init__(self, template):
        self.template = template

    def open(self, **kwargs):
        return urlopen(self.template.format_map(
                       kwargs))


# example use. download stock data from yahoo
# yahoo = UrlTemplate('http://finance.yahoo.com/d/quotes.csv?s={names}&f={fields}')
# for line in yahoo.open(names='IBM,AAPL,FB', fields= 'sl1c1v'):
#     print(line.decode('utf-8'))

# # 使用函数来简化
# def urltemplate(template):
#     def opener(**kwargs):
#         return urlopen(template.format_map(kwargs))
#     return opener
#
# # example use
# yahoo = urltemplate('http://finance.yahoo.com/d/quotes.csv?s={names}&f={fields}')
# for line in yahoo(names='IBM,AAPL,FB', fields= 'sl1c1v'):
#     print(line.decode('utf-8'))
#


# 带额外状态信息的回调函数
def apply_async(func, args, *, callback):
    result = func(*args)
    callback(result)

def print_result(result):
    print('Got:', result)


def add(x, y):
    return x +y

# apply_async(add, (2,3), callback=print_result)
# apply_async(add, ('hello', 'world'), callback=print_result)


# 回调函数访问外部信息 方法1 使用一个绑定方法
class ResultHander:
    def __init__(self):
        self.sequence = 0

    def handler(self, result):
        self.sequence += 1
        print('[{}] Got: {}'.format(self.sequence, result))

# r = ResultHander()
# apply_async(add,(2,3), callback=r.handler)
# apply_async(add, ('hello', 'world'), callback=r.handler)

# 方法二 作为类的替代 使用一个闭包捕获状态值
def make_handler():
    sequence = 0
    def handler(result):
        nonlocal sequence
        sequence += 1
        print('[{}] Got: {}'.format(sequence, result))
    return handler

# handler = make_handler()
# apply_async(add, (2,3), callback=handler)
# apply_async(add, ('hello', 'world'), callback=handler)

# 方法三 使用协程
def maker_handler2():
    sequence = 0
    while True:
        result = yield
        sequence += 1
        print('[{}] Got: {}'.format(sequence, result))

# handler = maker_handler2()
# next(handler) # advance to the yield
# apply_async(add, (2,3), callback=handler.send)
# apply_async(add, ('hello', 'world'), callback=handler.send)


# 内联回调函数
from queue import Queue
from functools import wraps

class Async:
    def __init__(self, func, args):
        self.func = func
        self.args = args


def inlined_async(func):
    @wraps(func)
    def wrapper(*args):
        f = func(*args)
        result_queue = Queue()
        result_queue.put(None)
        while True:
            result = result_queue.get()
            try:
                a = f.send(result)
                apply_async(a.func, a.args, callback=result_queue.put)
            except StopIteration:
                break
    return wrapper

@inlined_async
def test():
    r = yield Async(add, (2,3))
    print(r)
    r = yield Async(add, ('hello', 'world'))
    print(r)
    for n in range(10):
        r = yield Async(add, (n, n))
        print(r)
    print('Goodbye')


# test()

# if __name__ == '__main__':
#     import multiprocessing
#     pool = multiprocessing.Pool()
#     apply_async = pool.apply_async
#     test()


# 访问闭包中定义的变量
def sample():
    n = 0
    # Closure function
    def func():
        print('n=', n)

    # accessor methods for n
    def get_n():
        return n

    def set_n(value):
        nonlocal n
        n = value

    # attach as functions attributes
    func.get_n = get_n
    func.set_n = set_n
    return func

f = sample()
f()
f.set_n(10)
f()
f.get_n()


# 闭包模拟类的实例
import sys


class ClosureInstanc:
    def __init__(self, locals=None):
        if locals is None:
            locals = sys._getframe(1).f_locals
        # update instance dictionary with callables
        self.__dict__.update((key, value) for key, value in locals.items() if callable(value))

    # redirect special methods
    def __len__(self):
        return self.__dict__['__len__']()


# example use
def Stack():
    items = []
    def push(item):
        items.append(item)

    def pop():
        return items.pop()

    def __len__():
        return len(items)

    return ClosureInstanc()


class Stack2:
    def __init__(self):
        self.items = []

    def push(self, item):
        self.items.append(item)

    def pop(self):
        return self.items.pop()

    def __len__(self):
        return len(self.items)

from timeit import timeit

s = Stack()
s2 = Stack2()

if __name__ == '__main__':

    print(timeit('s.push(1);s.pop()', 'from __main__ import s'))
    # timeit('s=Stack();s.push(1);s.pop()', 'from __main__ import Stack')

    print(timeit('s2.push(1);s2.pop()', 'from __main__ import s2'))
    # timeit('s2=Stack2();s2.push(1);s.pop()', 'from __main__ import Stack2')


